﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LotteryVote.Entity;
using LotteryVote.Data;
using LotteryVote.Utility;
using LotteryVote.Logic.Bet;

namespace LotteryVote.Logic
{
    public class SettleResultFacade
    {
        #region Properties
        private BetFacade _betFacade;
        public BetFacade BetFacade
        {
            get
            {
                if (_betFacade == null)
                    _betFacade = new BetFacade();
                return _betFacade;
            }
            set
            {
                _betFacade = value;
            }
        }

        private UserDataAccess _daUser;
        internal UserDataAccess DaUser
        {
            get
            {
                if (_daUser == null)
                    _daUser = new UserDataAccess();
                return _daUser;
            }
            set
            {
                _daUser = value;
            }
        }

        private ShareRateFacade _dlShareRate;
        public ShareRateFacade DlShareRate
        {
            get
            {
                if (_dlShareRate == null)
                    _dlShareRate = new ShareRateFacade();
                return _dlShareRate;
            }
            set
            {
                _dlShareRate = value;
            }
        }

        private FreezeFundsFacade _blFreezeFunds;
        public FreezeFundsFacade BlFreezeFunds
        {
            get
            {
                if (_blFreezeFunds == null)
                    _blFreezeFunds = new FreezeFundsFacade();
                return _blFreezeFunds;
            }
            set
            {
                _blFreezeFunds = value;
            }
        }

        private SettleResultDataAccess _daSettleResult;
        internal SettleResultDataAccess DaSettleResult
        {
            get
            {
                if (_daSettleResult == null)
                    _daSettleResult = new SettleResultDataAccess();
                return _daSettleResult;
            }
            set
            {
                _daSettleResult = value;
            }
        }

        private IDictionary<int, IList<BetOrder>> _userBetOrderDic;
        internal IDictionary<int, IList<BetOrder>> UserBetOrderDic
        {
            get
            {
                if (_userBetOrderDic == null)
                    _userBetOrderDic = new Dictionary<int, IList<BetOrder>>();
                return _userBetOrderDic;
            }
        }
        private IDictionary<int, IList<SettleResult>> _memberSettleResult;
        protected IDictionary<int, IList<SettleResult>> MemberSettleResultDic
        {
            get
            {
                if (_memberSettleResult == null)
                    _memberSettleResult = new Dictionary<int, IList<SettleResult>>();
                return _memberSettleResult;
            }
        }

        private List<SettleResult> _settleResult;
        protected List<SettleResult> SettleResultList
        {
            get
            {
                if (_settleResult == null)
                    _settleResult = new List<SettleResult>();
                return _settleResult;
            }
        }
        #endregion

        #region Fields
        protected int SettleCompany
        {
            get;
            private set;
        }
        #endregion

        /// <summary>
        /// 结算今日注单.
        /// </summary>
        private IList<BetOrder> GetSettleBetOrder()
        {
            var orderList = BetFacade.GetTodayUnSettleOrder();
            foreach (var order in orderList)
            {
                int winmultiple = WinMultipleCalculator.GetCalculator().Calculate(order);   //获取输赢倍数
                decimal winResult;
                if (winmultiple > 0)
                    winResult = (order.Amount * (decimal)(order.Odds * winmultiple)) - order.NetAmount; //若倍数大于0，则为赢，赢钱=下注额×赔率×倍数-净额
                else
                    winResult = order.NetAmount * -1;       //若倍数小于等于0，则为输，输钱=净额×-1
                order.DrawResult = winResult;
            }
            return orderList;
        }
        public IList<SettleResult> Settle(int companyId)
        {
            SettleCompany = companyId;
            BetFacade.SettleBetOrder(companyId);
            var userList = DaUser.GetUserByRole(SystemRole.Company);
            userList.ForEach(it => GetSettleResult(it));
            DaSettleResult.ExecuteWithTransaction(() =>
            {
                SettleResultList.ForEach(it => DaSettleResult.Insert(it));
            });
            return SettleResultList;
        }
        /// <summary>
        /// 重新结算
        /// </summary>
        /// <param name="companyId">The company id.</param>
        public void ReSettle(int companyId)
        {
            //TODO:应该需要重新计算注单中奖金额
            DaSettleResult.Delete(companyId, DateTime.Today);
            Settle(companyId);
        }

        public IList<SettleResult> GetSettleResult(int userId, DateTime date)
        {
            return GetSettleResult(userId, date, date);
        }
        public IList<SettleResult> GetSettleResult(int userId, DateTime fromDate, DateTime toDate)
        {
            var settleResult = DaSettleResult.ListSettleResult(userId, fromDate, toDate);
            return MergerResult(settleResult);
        }

        /// <summary>
        /// 合并结果，将结果中同一个用户的记录汇总到一条
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns></returns>
        private IList<SettleResult> MergerResult(IList<SettleResult> source)
        {
            List<SettleResult> settleList = new List<SettleResult>();
            foreach (var gp in source.GroupBy(it => it.UserId))
            {
                SettleResult settle = null; ;
                bool isFirst = true; ;
                foreach (var settleItem in gp)
                {
                    if (isFirst)
                    {
                        isFirst = false;
                        settle = settleItem;
                        settleList.Add(settle);
                        continue;
                    }
                    settle.OrderCount += settleItem.OrderCount;
                    settle.BetTurnover += settleItem.BetTurnover;
                    settle.TotalCommission += settleItem.TotalCommission;
                    settle.Commission += settleItem.Commission;
                    settle.WinLost += settleItem.WinLost;
                    settle.TotalWinLost += settleItem.WinLost;
                    settle.UpperCommission += settleItem.UpperCommission;
                    settle.UpperWinLost += settleItem.UpperWinLost;
                    settle.UpperTotalWinLost += settleItem.UpperTotalWinLost;
                    settle.CompanyWinLost += settle.CompanyWinLost;
                }
            }
            return settleList;
        }

        private IList<SettleResult> GetSettleResult(User user)
        {
            List<SettleResult> memberResultList = new List<SettleResult>();     //用于盛放子用户的结余记录
            if (user.Role == SystemRole.Guest)                                  //会员结余计算
            {
                var result = BuildMemberSettleResult(user);
                if (result != SettleResult.Empty)
                {
                    SettleResultList.Add(result);                               //添加到总结余记录表容器中
                    memberResultList.Add(result);
                }
                return memberResultList;
            }

            var childUser = DaUser.GetChild(user);
            List<BetOrder> betOrderList = new List<BetOrder>();                 //用于存放本级用户可用到的注单
            foreach (var child in childUser)
            {
                memberResultList.AddRange(GetSettleResult(child));              //迭代直到所有Guest级别的用户都结余完毕
                betOrderList.AddRange(UserBetOrderDic[child.UserId]);           //将子用户的注单也添加到本级用户的注单容器中

                if (MemberSettleResultDic.ContainsKey(child.UserId))
                    AddMemberSettleResultToDic(user.UserId, MemberSettleResultDic[child.UserId]);
            }
            UserBetOrderDic.Add(user.UserId, betOrderList);

            if (user.Role == SystemRole.Company)        //到公司级别不进行上级结余计算（公司为最顶级了）
                return memberResultList;

            var userResultList = BuildUserSettleResult(user, memberResultList);
            SettleResultList.AddRange(userResultList);
            return userResultList;
        }
        /// <summary>
        /// 创建各级用户的结余结果.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="childSettleResultList">The child settle result list.</param>
        /// <returns></returns>
        private IList<SettleResult> BuildUserSettleResult(User user, IList<SettleResult> childSettleResultList)
        {
            var userWinLost = UserBetOrderDic[user.UserId].Sum(it => it.DrawResult);
            //用户总佣金，累加子用户所有订单中该用户的佣金
            var userTotalComm = UserBetOrderDic[user.UserId].Select(it => it.AncestorCommission).
                Select(it => it.Find(x => x.RoleId == (user.RoleId - 1))).Sum(it => it.CommAmount);     //总佣金=父亲用户佣金
            List<SettleResult> userResultList = new List<SettleResult>();
            var gs = childSettleResultList.GroupBy(it => it.ShareRate + it.UpperShareRate);     //用分成来进行分组
            foreach (var results in gs)
            {
                SettleResult result = new SettleResult();
                //var childWinLost = results.Sum(it => it.WinLost);                               //子用户输赢
                List<SettleResult> memberSettleResultList = new List<SettleResult>();
                foreach (var item in results)
                {
                    if (item == SettleResult.Empty) continue;
                    memberSettleResultList.AddRange(MemberSettleResultDic[item.UserId]);
                    //result.OrderCount += item.OrderCount;                                       //累加子用户的订单数量
                    result.BetTurnover += item.BetTurnover;                                     //累加子用户的下注额
                    result.WinLost += (item.WinLost + item.UpperWinLost);                       //累加子用户的输赢          
                    result.Commission += (item.Commission + item.UpperCommission);              //累加子用户的佣金
                    result.TotalWinLost += (item.TotalWinLost + item.UpperTotalWinLost);        //累加子用户的总输赢
                }
                var memberWinLost = memberSettleResultList.Distinct().Sum(it => it.WinLost);
                result.OrderCount = UserBetOrderDic[user.UserId].Count;
                result.TotalCommission = userTotalComm;
                result.ShareRate = results.Key;                                                 //本级分成数(相对级别，包含本用户及其子用户的分成)
                result.UpperShareRate = user.ParentId == 0 ? 0 : DaUser.GetParentShareRate(user.UserId);        //上级分成
                result.CompanyWinLost = CountCompanyWinLost(1 - result.UpperShareRate - result.ShareRate, memberWinLost, result.TotalCommission);
                result.UpperWinLost = memberWinLost * -1 * (decimal)result.UpperShareRate;       //上级输赢=子用户输赢（总）×上级成数
                result.UpperTotalWinLost = result.TotalWinLost / -1 - result.CompanyWinLost;    //上级总输赢=（总输赢/-1）-公司输赢
                result.UpperCommission = result.UpperTotalWinLost - result.UpperWinLost;        //上级佣金=上级总输赢-上级输赢
                result.UserId = user.UserId;
                result.CompanyId = SettleCompany;
                userResultList.Add(result);
            }

            return userResultList;
        }

        /// <summary>
        /// 创建会员的结余结果.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <returns></returns>
        private SettleResult BuildMemberSettleResult(User user)
        {
            //TODO:可以尝试一次性全部读出，再在内存中挑选
            var orderList = BetFacade.GetTodaySettleOrderByUser(user.UserId, SettleCompany, true);
            UserBetOrderDic.Add(user.UserId, orderList);
            if (orderList == null || orderList.Count == 0)
                return SettleResult.Empty;

            List<SettleResult> resultList = new List<SettleResult>();
            SettleResult result = new SettleResult();
            foreach (var order in orderList)
            {
                result.OrderCount += 1;
                result.BetTurnover += order.Turnover;
                result.Commission += order.Commission;
                result.TotalWinLost += order.DrawResult;
                result.TotalCommission += order.AncestorCommission.Find(it => it.RoleId == (int)SystemRole.Agent).CommAmount;
            }
            result.WinLost = result.TotalWinLost - result.Commission;


            //TODO:读取分成信息需优化
            var rate = DaUser.GetParentShareRate(user.UserId);

            result.UpperShareRate = rate;
            result.UpperWinLost = result.WinLost * -1 * (decimal)result.UpperShareRate;   //代理输赢=会员输赢×代理成数
            result.CompanyWinLost = CountCompanyWinLost(1 - result.UpperShareRate, result.WinLost, result.TotalCommission);
            result.UpperTotalWinLost = result.TotalWinLost / -1 - result.CompanyWinLost;         //代理总输赢=会员总输赢-公司输赢
            result.UpperCommission = result.UpperTotalWinLost - result.UpperWinLost;        //代理佣金=代理总输赢-代理输赢
            result.UserId = user.UserId;
            result.CompanyId = SettleCompany;

            AddMemberSettleResultToDic(user.UserId, result);
            return result;
        }
        /// <summary>
        /// 计算公司输赢.
        /// </summary>
        /// <param name="companyShareRate">The company share rate.</param>
        /// <param name="userWinLost">The user win lost.</param>
        /// <param name="totalCommission">The total commission.</param>
        /// <returns></returns>
        private decimal CountCompanyWinLost(double companyShareRate, decimal userWinLost, decimal totalCommission)
        {
            return ((userWinLost + totalCommission) * (decimal)companyShareRate) * -1;      //公司输赢=（会员输赢+总佣金）×公司成数×-1；
        }
        private void AddMemberSettleResultToDic(int userId, SettleResult result)
        {
            if (!MemberSettleResultDic.ContainsKey(userId))
                MemberSettleResultDic.Add(userId, new List<SettleResult>());
            MemberSettleResultDic[userId].Add(result);
        }
        private void AddMemberSettleResultToDic(int userId, IList<SettleResult> resultList)
        {
            if (!MemberSettleResultDic.ContainsKey(userId))
                MemberSettleResultDic.Add(userId, new List<SettleResult>());
            var settleResultList = MemberSettleResultDic[userId] as List<SettleResult>;
            settleResultList.AddRange(resultList);
        }
    }
}
